package main

import (
	"errors"
	"fmt"
)

/*
defer介绍。用于延迟一个函数或者方法（或者当前所创建的匿名函数）的执行

TIPS：defer语句只能出现在函数或方法的内部
*/
func main() {

	/*
		一. 在函数中可以添加多个defer语句。

		TIPS：如果有很多调用defer，当函数执行到最后时，这些defer语句会按照逆序执行（报错的时候也会执行），最后该函数返回
	*/

	/*	defer funcA()
		funcB()
		defer funcC()
		fmt.Println("主函数执行结束")*/

	/*
		二. defer语句经常被用于处理成对的操作，如打开-关闭、连接-断开连接、加锁-释放锁。特别是在执行打开资源的操作时，遇到错误需要提前返回，
			在返回前需要关闭相应的资源，不然很容易造成资源泄露等问题
	*/
	// TIPS：执行前将前面代码注释掉
	/*defer funcA()
	err := funcD()
	if err != nil {
		fmt.Println(err)
	}*/

	/*
		三. 方法中使用defer。延迟并不局限于函数，延迟一个方法调用也是完全合法的
	*/
	// TIPS：执行前将前面代码注释掉
	/*	person := Person{"STEVEN", 18}
		defer person.printPerson()
		fmt.Println("个人信息：")*/

	/*
		四. defer参数。延迟函数的参数在执行延迟语句时被执行，而不是在执行实际的函数调用时执行。
	*/
	// TIPS：执行前将前面代码注释掉
	/*	var newPerson Person
		newPerson.name = "修改前"
		newPerson.age = 10
		// TIPS：方法的本质是函数，所以这里直接用方法来测试验证
		defer newPerson.printPerson()
		newPerson.name = "修改后"
		newPerson.age = 15
		defer newPerson.printPerson()*/

	/*
		五. defer 实际上是堆栈的推迟。当一个函数有多个延迟调用时，它们被添加到一个堆栈中，并按后进先出（Last In First Out，LIFO）的顺序执行
	*/
	// TIPS：执行前将前面代码注释掉
	str := "我是steven"
	revertString(str)

	/**
	六. defer执行时机

	在Go语言的函数中return语句在底层并不是原子操作，它分为给返回值赋值和RET指令两步。而defer语句执行的时机就在返回值赋值操作后，RET指令执行前:
	执行示意图：https://www.liwenzhou.com/images/Go/func/defer.png
	*/
}

func funcA() {
	defer fmt.Println("funcA执行1")
	defer fmt.Println("funcA执行2")
	return
	fmt.Println("funcA执行3")
}

func funcB() {
	fmt.Println("funcB执行1")
}

func funcC() {
	fmt.Println("funcC执行1")
	defer fmt.Println("funcC执行2")
	// 停止当前程序
	panic(any("funcC执行painc"))
}

func funcD() error {
	fmt.Println("funcD执行")
	return errors.New("主动抛个异常出来")
}

type Person struct {
	name string
	age  int
}

func (person Person) printPerson() {

	fmt.Println(person)
}

/*
利用defer特性实现字符串倒序
*/
func revertString(str string) {

	for _, i2 := range str {
		defer fmt.Printf("%c", i2)
	}
}
